//! contract message 智能合约消息
//!

use crate::core::constant;
use crate::error::ErrorKind;
use crate::{
    bean::message::{base_message::BaseMessage, message_type::MessageType},
    error::Error,
    mnemonic::Mnemonic,
    utils::sign,
};
use base64::Engine;
use hdwallet::secp256k1::Secp256k1;
use serde::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut};

#[derive(Debug, Clone, Default)]
pub struct ContractMessage {
    // 基础消息结构
    base_message: BaseMessage,
    // 合约相关内容
    data: Vec<u8>,
    // 合约消息hash值.
    hash: String,
    //所属打包Event的hash值。
    event_hash: String,
}

impl ContractMessage {
    pub fn new(words: &str, data: &[u8], from_address: &str) -> Result<Self, Error> {
        debug_assert!(!words.is_empty(), "words can no be empty!");
        debug_assert!(!data.is_empty(), "data can not be empty!");
        debug_assert!(!from_address.is_empty(), "from_address can not be empty!");
        let mut bm = BaseMessage::default();
        bm.set_msg_type(MessageType::Contract);
        bm.set_from_address(from_address);
        bm.set_timestamp(chrono::Local::now().timestamp());
        let mut new = Self {
            base_message: bm,
            data: data.to_vec(),
            hash: "".to_string(),
            event_hash: "".to_string(),
        };
        new.sign_message(words, "")?;
        Ok(new)
    }

    /// 对数据结构进行签名
    /// words: 助记词
    /// passphrase: 键入值
    fn sign_message(&mut self, words: &str, passphrase: &str) -> Result<(), Error> {
        let mnemonic = Mnemonic::new(words, passphrase)?;
        let keys = mnemonic.keypair(44, 0, 0, 0, 0.into())?;
        let private_key = keys.secret_key();
        let pub_key = private_key.public_key(&Secp256k1::new());
        self.set_pub_key(&base64::engine::general_purpose::STANDARD.encode(&pub_key.serialize()));
        let sig = sign::sign(&self.get_message()?, &private_key)?;
        self.set_signature(&sig);
        Ok(())
    }

    /// 生成json消息字符串
    /// 获取签名数据结构
    pub fn get_message(&self) -> Result<String, Error> {
        let mut inner = Inner::default();

        if self.from_address().is_empty() {
            return Err(Error::from_kind(ErrorKind::Msg(
                "from_address is illegal".to_string(),
            )));
        }
        inner.from_address = self.from_address().to_string();
        if chrono::Local::now().timestamp() - self.timestamp()
            > constant::MESSAGE_PRESEND_TIME_OUT as i64
        {
            return Err(Error::from_kind(ErrorKind::Msg(
                "timestamp is illegal".to_string(),
            )));
        }
        inner.timestamp = self.timestamp();

        if self.pub_key().is_empty() {
            return Err(Error::from_kind(ErrorKind::Msg(
                "pubkey is illegal".to_string(),
            )));
        }
        inner.pubkey = self.pub_key().to_string();

        if self.msg_type() != MessageType::Contract {
            return Err(Error::from_kind(ErrorKind::Msg(
                "type is illegal".to_string(),
            )));
        }
        inner.r#type = self.msg_type();

        if self.data.is_empty() {
            return Err(Error::from_kind(ErrorKind::Msg(
                "data is illegal".to_string(),
            )));
        }
        let data_base64 = base64::engine::general_purpose::STANDARD.encode(&self.data);
        inner.data = data_base64;

        if self.signature().is_empty() {
            inner.signature = None;
            Ok(serde_json::to_string(&inner)?)
        } else {
            inner.signature = Some(self.signature().to_string());
            let m = Message { message: inner };
            Ok(serde_json::to_string(&m)?)
        }
    }

    pub fn data(&self) -> &[u8] {
        &self.data
    }

    pub fn hash(&self) -> &str {
        &self.hash
    }

    pub fn event_hash(&self) -> &str {
        &self.event_hash
    }

    pub fn set_data(&mut self, data: &[u8]) {
        self.data = data.to_vec();
    }

    pub fn set_hash(&mut self, hash: &str) {
        self.hash = hash.to_string();
    }

    pub fn set_event_hash(&mut self, event_hash: &str) {
        self.event_hash = event_hash.to_string();
    }
}

impl Deref for ContractMessage {
    type Target = BaseMessage;

    fn deref(&self) -> &Self::Target {
        &self.base_message
    }
}

impl DerefMut for ContractMessage {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.base_message
    }
}

/// json: {"message": {"from_address": "", "timestamp": "", ....}}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Message {
    message: Inner,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Inner {
    from_address: String,
    timestamp: i64,
    pubkey: String,
    r#type: MessageType,
    data: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    signature: Option<String>,
}

#[cfg(test)]
mod tests {
    use crate::bean::message::contract_message::ContractMessage;
    use crate::mnemonic::Mnemonic;

    #[test]
    fn test_contract_message() {
        let mne = Mnemonic::new("", "hello").unwrap();
        let words = mne.mnemonic();
        let cm = ContractMessage::new(words, "你好世界".as_bytes(), "192.168.0.1").unwrap();
        println!("{:?}", cm);

        let json = cm.get_message().unwrap();
        println!("{}", json);
    }
}
